﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Web.Services;
using System.Xml.Serialization;
//using SharedData;

namespace OverlakeServer_SOAP
{
    /// <summary>
    /// Summary description for OverlakeAppService
    /// </summary>
    [WebService(Namespace = "http://app.overlake.org/")]          // Turn this off for a while
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public class OverlakeAppService_1_0 : System.Web.Services.WebService
    {
        private const string TeacherDataUrl = "http://portal.overlake.org/senior_systems/faculty";
        private const string EmailSuffix = "@overlake.org";
        private const string PhonePrefix = "+1 (425) 868-6191";
        private const string PhonePrefixNoExtention = "+1 (425) 868-1000";
        private const string DayPrefix = "Day ";
        private const string IncorrectUserPassError = "Incorrect username or password.\nPlease try again.";
        private const string LunchDataUrl = "http://portal.overlake.org/cafeteria/callback/mobile";
        private const string SchoolName = "Upper";
        private const string WeatherDataUrl = "http://api.wxbug.net/getLiveWeatherRSS.aspx?ACode=A5545972477&stationid=RDVRL&unittype=0&outputtype=1";
        private const string TestResponse = "Overlake";
        private const string ErrorLogFilePrefix = @"C:\OverlakeApp\Errors\";
        private const string ErrorLogFileSuffix = ".xml";

        private static readonly WebServices.AcademicService.AcademicServiceSoapClient AcademicService = new WebServices.AcademicService.AcademicServiceSoapClient();
        //private static readonly WebServices.DataExportService.DataExportServiceSoapClient dataExportService = new WebServices.DataExportService.DataExportServiceSoapClient();
        private static readonly WebServices.UserManagementService.UserManagementServiceSoapClient UserManagementService = new WebServices.UserManagementService.UserManagementServiceSoapClient();
        private static readonly WebServices.UserProfileService.UserProfileServiceSoapClient UserProfileService = new WebServices.UserProfileService.UserProfileServiceSoapClient();

        #region Calendar

        /// <summary>
        /// Gets calendar events from <paramref name="start"/> to <paramref name="end"/>.
        /// </summary>
        /// <param name="start">The start of the range.</param>
        /// <param name="end">The end of the range.</param>
        /// <returns>A list of calendar items from the range.</returns>
        [WebMethod]
        public List<SharedData.CalendarEvent> GetCalendar(DateTime start, DateTime end)
        {
            start = start.AddDays(-1);
            return CalendarFactory.GetFilteredCalendarItems(start, end);
        }

        #endregion

        #region Login
        /// <summary>
        /// Logs the user in, and returns their corresponding UserInfo object.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        [WebMethod]
        public SharedData.UserInfo Login(string username, string password)
        {
            SharedData.UserInfo user = new SharedData.UserInfo();

            string agent = Context.Request.Headers["user-agent"];
            if (String.IsNullOrEmpty(agent))
            {
                agent = "unknown";
            }

            Logger.LogLogin(username, agent, this.Context.Request.UserHostAddress);

            try
            {
                user.Key = UserManagementService.login(username, password);

                if (string.IsNullOrEmpty(user.Key))
                {
                    user.State = SharedData.UserInfo.LoginState.BadPassword;
                    user.ErrorMessage = IncorrectUserPassError;
                }
                else
                {
                    var userInfo = UserProfileService.getUserInfo(user.Key, false, true);

                    if (userInfo.StudentAccounts.Length > 0)
                    {
                        user.StudentID = userInfo.StudentAccounts[0].StudentID;
                    }

                    if (user.StudentID == "STUD011")
                        user.StudentID = "KENN011";

                    user.State = SharedData.UserInfo.LoginState.Success;
                }
            }
            catch (Exception exc)
            {
                user.State = SharedData.UserInfo.LoginState.Error;
                user.ErrorMessage = exc.Message;
            }

            return user;
        }
        #endregion

        #region Lunch
        [System.Runtime.Serialization.DataContract]
        private class LunchContainer
        {
            [System.Runtime.Serialization.DataMember]
#pragma warning disable
            public SharedData.Lunch[] data;
#pragma warning enable
        }

        private static DataContractJsonSerializer lunchDeserializer = new DataContractJsonSerializer(typeof(LunchContainer));

        private static Cache<List<SharedData.Lunch>> lunchCache = new Cache<List<SharedData.Lunch>>(() =>
        {
            WebRequest request = HttpWebRequest.Create(LunchDataUrl);

            LunchContainer lunches = (LunchContainer)lunchDeserializer.ReadObject(request.GetResponse().GetResponseStream());

            if (lunches.data != null)
                return lunches.data.ToList();

            return null;
        }, TimeSpan.FromHours(6));

        [WebMethod]
        public List<SharedData.Lunch> GetLunchData()
        {
            return lunchCache.Item;
        }
        #endregion

        #region Schedule

        /// <summary>
        /// Gets the assembled schedule.
        /// </summary>
        /// <param name="key">The user key.</param>
        /// <param name="userId">The userid.</param>
        /// <returns>A populated ScheduleInfo object.</returns>
        [WebMethod]
        public SharedData.ScheduleInfo GetAssembledSchedule(string key, string userId)
        {
            var sections = ConvertSections(AcademicService.GetStudentSectionsByStudentID(SchoolName, userId, false, key), key).OrderBy(s => s.MeetsGradeSeq);

            var allLines = ConvertLines((from s in sections
                                         select AcademicService.GetSectionSchedule(SchoolName, s.SectionID, false, key)).SelectMany(a => a));

            var schedule = from line in allLines
                           orderby line.BlockName
                           group line by line.DayName into day
                           orderby day.Key

                           select new SharedData.ScheduleDay()
                           {
                               DayLetter = day.Key,
                               DaySchedule = day.OrderBy(l => l.StartTime).ToArray()
                           };

            return new SharedData.ScheduleInfo() { Sections = sections.ToArray(), Schedule = schedule.ToArray() };
        }

        private static readonly Dictionary<string, SharedData.Section> SectionCache = new Dictionary<string, SharedData.Section>();
        private IEnumerable<SharedData.Section> ConvertSections(WebServices.AcademicService.Section[] sections, string key)
        {
            SharedData.Section[] newSections = new SharedData.Section[sections.Length];

            for (int i = 0; i < sections.Length; i++)
            {
                var s = sections[i];

                if (!SectionCache.ContainsKey(s.SectionID))
                {
                    s = AcademicService.GetSection(SchoolName, sections[i].SectionID, false, key);

                    newSections[i] = new SharedData.Section()
                    {
                        CourseID = s.CourseID,
                        CourseName = s.CourseName,
                        Department = s.Department,
                        FacultyIDs = (from f in s.FacultyEntityNumbers              // HACKHACKHACKHACKHACK
                                      select teacherDataCache.Item.First(t => t.FacultyEntityNumber == f).FacultyId).ToArray(),
                        MeetsGradeSeq = s.MeetsGradeSeq,
                        MeetsPeriod = s.MeetsPeriod,
                        MeetTerms = s.MeetTerms,
                        SectionID = s.SectionID
                    };
                    SectionCache.Add(s.SectionID, newSections[i]);
                }

                newSections[i] = SectionCache[s.SectionID];
            }

            return newSections;
        }

        private SharedData.Section GetSection(string sectionId)
        {
            return SectionCache.ContainsKey(sectionId) ? SectionCache[sectionId] : null;
        }

        private IEnumerable<SharedData.ScheduleLine> ConvertLines(IEnumerable<WebServices.AcademicService.SectionScheduleLine> lines)
        {
            foreach (var l in lines)
            {
                var section = this.GetSection(l.SectionID);
                section.Room = l.Room;
                yield return new SharedData.ScheduleLine()
                {
                    BlockName = l.BlockName,
                    CourseName = section.CourseName,
                    DayName = l.DayName,
                    EndTime = l.EndTime.ToTimeSpanTicks(),
                    StartTime = l.StartTime.ToTimeSpanTicks(),
                    SectionID = l.SectionID
                };
            }
        }

        #endregion

        #region Teachers
        [DataContract]
        private class TeacherCollection
        {
            [DataMember(Name = "data")]
            public SharedData.Teacher[] Teachers { get; set; }
        }

        private static readonly DataContractJsonSerializer teacherSerializer = new DataContractJsonSerializer(typeof(TeacherCollection));

        private static readonly Cache<SharedData.Teacher[]> teacherDataCache = new Cache<SharedData.Teacher[]>(() =>
        {
            WebRequest request = WebRequest.Create(TeacherDataUrl);

            var teachers = (TeacherCollection)teacherSerializer.ReadObject(request.GetResponse().GetResponseStream());

            foreach (var t in teachers.Teachers)
            {
                string email = t.WebUserId.ToLower() + EmailSuffix;

                try
                {
                    var emails = t.Email.Split(';').Select(s => s.Trim());
                    var emails2 = t.Email.Split(';').Select(s => s.Trim());

                    if (emails.Concat(emails2).Any(e => e.EndsWith(EmailSuffix)))
                    {
                        email = t.Email.Split(';').FirstOrDefault(s => s.EndsWith(EmailSuffix));
                    }
                }
                catch { }

                t.Email = email;

                if (string.IsNullOrEmpty(t.Phone))
                {
                    t.Phone = PhonePrefixNoExtention;
                }
                else
                {
                    t.Phone = PhonePrefix + " x" + t.Phone;
                }

            }

            return teachers.Teachers.Where(t => !string.IsNullOrEmpty(t.FirstName)).OrderBy(t => t.LastName).ToArray();
        }, TimeSpan.FromHours(24));

        [WebMethod]
        public SharedData.Teacher[] GetTeachers(string key)
        {
            return teacherDataCache.Item;
        }

        #endregion

        #region Weather

        private static Cache<weather> _CurrentWeather = new Cache<weather>(() => GetWeather(WeatherDataUrl), new System.TimeSpan(0, 5, 0));

        public static weather CurrentWeather
        {
            get
            {
                return _CurrentWeather.Item;
            }
        }

        private static weather GetWeather(string url)
        {
            WebRequest request = HttpWebRequest.Create(url);

            return (weather)new XmlSerializer(typeof(weather)).Deserialize(request.GetResponse().GetResponseStream());
        }



        [WebMethod]
        public SharedData.WeatherData GetWeatherData()
        {
            weather current = CurrentWeather;

            return new SharedData.WeatherData(current.ob[0].temp[0].Value,
                                              current.ob[0].temp[0].units,
                                              current.ob[0].humidity[0].Value);
        }

        static Cache<byte[]> weatherImageCache = new Cache<byte[]>(() =>
        {
            return GetBufferFromImage(CurrentWeather.ob[0].currentcondition[0].icon);
        }, new TimeSpan(0, 0, 30));

        [WebMethod]
        public byte[] GetWeatherImage()
        {
            return weatherImageCache.Item;
        }

        private static byte[] GetBufferFromImage(string imageLnk)
        {
            var str = imageLnk.Split('/')[6].Split('.')[0];

            imageLnk = string.Format("http://img.weather.weatherbug.com/forecast/icons/localized/280x235/en/trans/{0}.png", str);

            var request = (HttpWebRequest)WebRequest.Create(imageLnk);
            var response = (HttpWebResponse)request.GetResponse();
            var myImage = Image.FromStream(response.GetResponseStream());
            var imageStream = new MemoryStream();
            myImage.Save(imageStream, ImageFormat.Png);
            imageStream.Position = 0;
            var buffer = new byte[(int)imageStream.Length];
            imageStream.Read(buffer, 0, (int)imageStream.Length);
            return buffer;
        }

        #endregion

        #region Test

        [WebMethod]
        public string Test()
        {
            return TestResponse;
        }
        #endregion

        #region Error Reporting

        [WebMethod]
        public bool SubmitError(SharedData.ErrorReport report)
        {
            using (StreamWriter str = File.CreateText(ErrorLogFilePrefix + DateTime.Now.ToString("yyyyMMdd-HHmm") + ErrorLogFileSuffix))
            {
                XmlSerializer ser = new XmlSerializer(typeof(SharedData.ErrorReport));
                ser.Serialize(str, report);
            }

            return true;
        }

        #endregion

    }

    static class Ext_DateTime
    {
        public static long ToTimeSpanTicks(this WebServices.AcademicService.Time t)
        {
            return new DateTime().AddMinutes(t.Minute).AddHours(t.Hour).TimeOfDay.Ticks;
        }
    }
}
